package com.zmn.uac.business.impl.oauth.login;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipayEncrypt;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayUserInfoShareRequest;
import com.alipay.api.response.AlipayUserInfoShareResponse;
import com.zmn.common.dto2.ResponseDTO;
import com.zmn.common.utils.string.StringUtil;
import com.zmn.saas.common.dro.app.AppInfoDRO;
import com.zmn.saas.dubbo.dto.app.AppDRO;
import com.zmn.tapi.common.alipay.AlipaySystemOauthToken;
import com.zmn.uac.business.interfaces.oauth.login.AliPayLoginBService;
import com.zmn.uac.business.template.oauth.login.BaseLoginTemplateMode;
import com.zmn.uac.client.AlipayClient;
import com.zmn.uac.common.constant.AliPayConstant;
import com.zmn.uac.common.dio.oauth.login.TempCodeDIO;
import com.zmn.uac.common.dio.oauth.login.UserAuthLoginDIO;
import com.zmn.uac.common.entity.oauth.login.*;
import com.zmn.uac.common.entity.oauth.phone.UserPhoneInfo;
import com.zmn.uac.common.info.UserAuthPhoneInfo;
import com.zmn.uac.common.info.alipay.AliPayUserAuthPhoneInfo;
import com.zmn.uac.common.info.alipay.AliPayUserDecryptPhoneInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Objects;

/**
 * 类描述：支付宝登录业务实现
 *
 * @author xujie
 * @since 2021/05/18 18:55
 */
@Slf4j
@Service
public class AliPayLoginBServiceImpl extends BaseLoginTemplateMode implements AliPayLoginBService {

    private static final String TAG = "支付宝登录业务实现";

    @Resource
    AlipayClient alipayClient;

    @Override
    public ResponseDTO<UserAuthLoginInfo> thirdPartyUserDataAcquisition(UserAuthLoginDIO dio,
                                                                        UserAuthPhoneInfo authPhoneInfo) {
        String uuid = dio.getUuid();
        log.info("[{}], {}, 支付宝小程序用户授权三方注册入参:{}", TAG, uuid, JSONObject.toJSONString(dio));
        Integer channelId = dio.getChannelId();
        String code = dio.getCode();
        AppInfoDRO app = getThirdAppByChannelId(channelId, dio.getShopId());
        log.info("[{}], {},获取SaaS配置信息, AppInfoDRO:[{}]", TAG, uuid, JSONObject.toJSONString(app));

        if (app == null) {
            return ResponseDTO.fail("查询应用为null");
        }
        String thirdAppId = app.getThirdAppId();
        String thirdAppSecret = app.getThirdAppSecret();
        String thirdAppSecretPublic = app.getThirdAppSecretPublic();

        AlipaySystemOauthToken oauthToken = alipayClient.getAlipaySystemOauthToken(thirdAppId, thirdAppSecret, thirdAppSecretPublic, code, null);
        if (Objects.isNull(oauthToken)) {
            return ResponseDTO.fail("支付宝小程序:获取Token失败");
        }

        AlipayUserInfoShareResponse userInfoResponse = getAliPayUserInfoResponse(thirdAppId, thirdAppSecret, thirdAppSecretPublic, oauthToken.getAccessToken());
        if (Objects.isNull(userInfoResponse) || !userInfoResponse.isSuccess()) {
            log.error("[{}], {}, 支付宝小程序: 请求支付宝授权接口异常, 获取用户数据失败：{}", TAG, uuid, JSONObject.toJSONString(userInfoResponse));
        }
        if (Objects.isNull(userInfoResponse)) {
            userInfoResponse = new AlipayUserInfoShareResponse();
        }
        String channelName = getNameByChannelId(channelId);
        Integer userType = "1".equals(userInfoResponse.getUserType()) ? 2 : 1;
        Integer gender = "M".equals(userInfoResponse.getGender()) ? 1 : "F".equals(userInfoResponse.getGender()) ? 2 : 3;
        UserPhoneInfo userPhoneInfo;
        if (StringUtil.isBlank(authPhoneInfo.getMobile())) {
            String customFieldValue = app.getCustomFieldValue();
            String decryptKey = null;
            if (StringUtil.isBlank(customFieldValue)) {
                log.error("[{}], 支付宝小程序登录获取手机号，应用扩展字段为null，channelId:{}", TAG, channelId);
            } else {
                String decryptKeyStr = "decryptKey";
                JSONObject jsonObject = JSON.parseObject(customFieldValue);
                log.info("[{}], 支付宝小程序登录获取手机号，应用扩展字段:{}，channelId:{}", TAG, customFieldValue, channelId);
                if (jsonObject.containsKey(decryptKeyStr)) {
                    decryptKey = jsonObject.getString(decryptKeyStr);
                }
            }
            ResponseDTO<UserPhoneInfo> userPhoneInfoResponse = decryptPhoneInfo(authPhoneInfo, thirdAppSecretPublic, dio.getUuid(), decryptKey);
            if (userPhoneInfoResponse.isSuccess()) {
                userPhoneInfo = userPhoneInfoResponse.getData();
            } else {
                userPhoneInfo = new UserPhoneInfo();
            }
        } else {
            userPhoneInfo = UserPhoneInfo.builder().mobile(authPhoneInfo.getMobile()).build();
        }

        UserInfo info = setUserInfo(oauthToken, userType, userInfoResponse, gender);
        PlatInfo platInfo = setPlatInfo(dio, app, channelId, channelName);
        ThirdAppInfo thirdAppInfo = setThirdAppInfo(thirdAppId, app, thirdAppSecret);
        UserAuthLoginInfo userAuthLoginInfo = setUserAuthLoginInfo(info, platInfo, thirdAppInfo, userPhoneInfo);

        return ResponseDTO.success(userAuthLoginInfo);
    }

    /**
     * 获取用户openid
     *
     * @param tempCodeDIO
     * @param app
     * @return
     */
    @Override
    public ResponseDTO<String> getThirdPartyOpenIdByCode(TempCodeDIO tempCodeDIO, AppDRO app) {
        AlipaySystemOauthToken token = alipayClient.getAlipaySystemOauthToken(app.getThirdAppId(), app.getThirdAppSecret(), app.getThirdAppSecretPublic(), tempCodeDIO.getCode(), null);
        if (Objects.isNull(token)) {
            return ResponseDTO.fail("支付宝小程序:获取Token失败");
        }

        return ResponseDTO.success(token.getUserId());
    }

    private AlipayUserInfoShareResponse getAliPayUserInfoResponse(String thirdAppId, String thirdAppSecret, String thirdAppSecretPublic, String accessToken) {

        try {
            return new DefaultAlipayClient(AliPayConstant.SERVER_URL, thirdAppId
                    , thirdAppSecret, AliPayConstant.FORMAT
                    , AliPayConstant.CHARSET, thirdAppSecretPublic
                    , AliPayConstant.SIGN_TYPE).execute(new AlipayUserInfoShareRequest(), accessToken);
        } catch (AlipayApiException e) {
            log.error("{},支付宝接口调用失败,token:{},异常信息:{}", TAG, accessToken, e.getMessage(), e);
            return null;
        }
    }

    public ResponseDTO<UserPhoneInfo> decryptPhoneInfo(UserAuthPhoneInfo authPhoneInfo, String aliPayPublicKey, String uuid, String decryptKey) {

        // https://opendocs.alipay.com/mini/2019110100244259
        // https://opendocs.alipay.com/mini/01xa7y
        if (StringUtil.isBlank(decryptKey)) {
            log.error("[{}], uuid：{}，decryptKey秘钥为null", TAG, uuid);
            return ResponseDTO.fail("decryptKey秘钥为null");
        }
        AliPayUserAuthPhoneInfo aliPayUserAuthPhoneInfo = (AliPayUserAuthPhoneInfo) authPhoneInfo;
        String signType = StringUtil.isBlank(aliPayUserAuthPhoneInfo.getSignType()) ? "RSA2" : aliPayUserAuthPhoneInfo.getSignType();
        String encryptType = StringUtil.isBlank(aliPayUserAuthPhoneInfo.getEncryptType()) ? "AES" : aliPayUserAuthPhoneInfo.getEncryptType();
        String charset = StringUtil.isBlank(aliPayUserAuthPhoneInfo.getCharset()) ? "UTF-8" : aliPayUserAuthPhoneInfo.getCharset();
        String sign = aliPayUserAuthPhoneInfo.getSign();
        String content = aliPayUserAuthPhoneInfo.getResponse();
        // 如果密文的
        boolean isDataEncrypted = !content.startsWith("{");
        boolean signCheckPass;
        // 2. 验签
        String signContent = content;
        //String signVeriKey = "你的小程序对应的支付宝公钥（为扩展考虑建议用appId+signType做密钥存储隔离）";
        //String decryptKey = "你的小程序对应的加解密密钥（为扩展考虑建议用appId+encryptType做密钥存储隔离）";
        // 如果是加密的报文则需要在密文的前后添加双引号
        if (isDataEncrypted) {
            signContent = "\"" + signContent + "\"";
        }
        try {
            signCheckPass = AlipaySignature.rsaCheck(signContent, sign, aliPayPublicKey, charset, signType);
        } catch (AlipayApiException e) {
            // 验签异常, 日志
            log.error("[{}], uuid：{}，验签异常：{}", TAG, uuid, e.getMessage(), e);
            return ResponseDTO.fail("支付宝获取授权手机号，验签异常。");
        }
        if (!signCheckPass) {
            // 验签不通过（异常或者报文被篡改），终止流程（不需要做解密）
            // "验签失败"
            log.error("[{}], uuid：{}，验签不通过", TAG, uuid);
            return ResponseDTO.fail("支付宝获取授权手机号，验签不通过。");
        }
        //3. 解密
        String plainData;
        if (isDataEncrypted) {
            try {
                plainData = AlipayEncrypt.decryptContent(content, encryptType, decryptKey, charset);
            } catch (AlipayApiException e) {
                // 解密异常, 记录日志
                log.error("[{}], uuid：{}，解密异常：{}", TAG, uuid, e.getMessage(), e);
                return ResponseDTO.fail("支付宝获取授权手机号，解密异常。");
            }
        } else {
            plainData = content;
        }
        log.info("[{}], uuid：{}，解密手机号信息：{}", TAG, uuid, plainData);
        AliPayUserDecryptPhoneInfo phoneInfo = JSONObject.parseObject(plainData, AliPayUserDecryptPhoneInfo.class);
        if (StringUtil.isMobile(phoneInfo.getMobile())) {
            UserPhoneInfo userPhoneInfo = UserPhoneInfo.builder()
                    .mobile(phoneInfo.getMobile())
                    .build();
            return ResponseDTO.success(userPhoneInfo);
        } else {
            log.error("[{}], uuid：{}，解密手机号信息，校验手机号格式失败：{}", TAG, uuid, phoneInfo);
            return ResponseDTO.fail("校验手机号格式失败");
        }
    }

    private UserInfo setUserInfo(AlipaySystemOauthToken oauthToken,Integer userType,AlipayUserInfoShareResponse userInfoResponse,Integer gender){
        return UserInfo.builder()
                .userId(null)
                .openId(oauthToken.getUserId())
                .unionId(oauthToken.getUserId())
                .type(userType)
                .realname(StringUtil.isBlank(userInfoResponse.getNickName()) ? "支付宝用户" : userInfoResponse.getNickName())
                .username(null)
                .nickName(userInfoResponse.getNickName())
                .avatarSrc(userInfoResponse.getAvatar())
                .age(null)
                .gender(gender)
                .country(userInfoResponse.getCountryCode())
                .province(userInfoResponse.getProvince())
                .city(userInfoResponse.getCity())
                .build();
    }

    private PlatInfo setPlatInfo(UserAuthLoginDIO dio,AppInfoDRO app,Integer channelId,String channelName){
        return PlatInfo.builder()
                .shopId(dio.getShopId())
                .plat(app.getPlat())
                .channelId(channelId)
                .channelName(channelName)
                .entrance(dio.getEntrance())
                .build();
    }

    private ThirdAppInfo setThirdAppInfo(String thirdAppId,AppInfoDRO app,String thirdAppSecret){
        return ThirdAppInfo.builder()
                .appId(thirdAppId)
                .appName(app.getName())
                .appSecret(thirdAppSecret)
                .build();
    }

    private UserAuthLoginInfo setUserAuthLoginInfo(UserInfo info,PlatInfo platInfo,ThirdAppInfo thirdAppInfo,UserPhoneInfo userPhoneInfo){
        return UserAuthLoginInfo.builder()
                .userInfo(info)
                .userOtherInfo(new UserOtherInfo())
                .platInfo(platInfo)
                .thirdAppInfo(thirdAppInfo)
                .userPhoneInfo(userPhoneInfo)
                .build();
    }

    public static void main(String[] args) throws AlipayApiException {
        //String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCiTAvg/CMHlApL8iVR8u/jpXUluq1Wec/difny/pASACrQTQsdqBLlRE+Maa/HiZpgZ5W5ldf/O1tdwth1hs9tYsm4OaciwXcMHFoeQOwpqGwYTAxXpdalxec44nMOVvN3ZoS4FXnuV4Dv7Im310rFqhPdZxjOKjwZIOwD8i6FMqCvRwkUof2CHqraTEwnHdXJV7FFz+kQEp51jQ6kHQoyG5XP2QTnA+VMWqKbYfOR0Psvi3Sg3/JZp4DdR9JUihHRbHPNRHhJDpGxZ+pfhK3W/auaKDBOhkagOKra0AFper1f/E/Tl4YFOjLgpdcpEyM8vzK8z5ks12Irll9LrRhxAgMBAAECggEAdSJ1oFfZ4GpUVCPcE51GzgJo3mYxHKyl/brLjzlzOcqtG9G/KBRaLuZyk2Dj4RvLM7EPOvaPAkMQ2kb6ezhlFQmrlS7nFgqTpUYBNLyetiI8gfzBG7Exjkubak7lw3+2EIMJXuMBMaguAvuxGeJksOzjkXQLTyDkZy5/qQlt0etmDwNpj/OdqqKudkkCRR2DaU2Il14Ild1xoF/EHnTo8NjaakbupJYz5wbmhNXvto3zFPZh5Q9cCQ5XmIhdyQGT9emYS0AFg2pFL8rOnpZzW9TVVDkgKi/VZAs4l46za8slm4JP/ptsXjBqf6uv1YYIYLmXdR5LwFr3zunZSq6m0QKBgQDPeMqMrQTSqVv29vVRzFS+Fv0h9daqNS+2VTxQgbJO+XbyMOv9OjkgbF+XWw5BacNtxpBd2w+lm366gBYD9aSxx2ZK5tOw7U2e0caV8m64CnacWZ8DUWsn2HAl/L2YrZnCZLGc/POD8WQ3xhbRTJ3zZMQmg0MlUubJcDe6BJiMWwKBgQDIQjvWul8y8/MrQXN3EDSp8ULB26kk8RDOM9XWoQ7rxUQAdOXWZ4KyW7urN6++pTJ0hQInGV9JKyCyPIxJX4b65cZvRtIFqtxJyIfbgjWfRuiame7hgI8YsptxN3x+t0Dxa4VZOmGHiP8LnxqeX0XaDiLLXwkJwbMn3YBUlro4IwKBgAnV/SxMK5VdKBn+PUUifcYBSwDoKcQp/1gSlGhwraZm10fGH/oCX6D04ldHntZlEDnNFx0L/KLqS0jGN8T6SVOQU34rkU+rAGoOIe8L7knVXSgua4Z5vq3F6hMy9z4cxNRdd7osl9Lih9DJGF3V1wsp9M6qnG1Rj9Vu94VfIGhPAoGBAJt7KUB/dRh3ZW5JI9/mWi0z3zPqWzoOXJDsl/NLGu2dPD3qhORhe40FQgEefDnVwwXKmzBGN7/0Vy9raTQykfdM3TNaVggGb4BtzGV2hDGE+WXHbDvy96ciTSOrA+LSXv7SDzeG52A20mZwgldB+1bgsM0bnUXQ+UtTY6bJXA3nAoGABKi7ShvyYPJh8CqzwRKPoF1dmMcSoR7lnVUqfrpo1sb+zHkqd0jQ37LwkqcUdznZ8rWw29gvQ+1QFumKfA6J3qpyCWwwwBzEUS7A7O/oiE2d+25ebM8rvzWnYYkS2OCnToIZJhglXntUs5yC4UusJnB6J4dbHL06sFlcSCw7y6g=";
        String content = "M6B6gBYKa1yckpZ0qhDI01H0oGPbtjtW/fkyQqs1sQ4prc8pkG0un9RXG5X2fOK6nefQeQttSdKW+VeZHdrfrYya9LjyrUeYrHY5inAk98/NpOP+7V2zL4HwjvK1Q/EnJmryuLCbpNOZE+UsvCiVumB22A4N5nGnaSiItV/OAtJBC4hbnvFJRFpUcJh6BR4uh5bDGQ2DjUW5VYw5a5rb5/0YX67WaqF2fA9NgnVOOyGT4m7yjapp23GEKGn1oygcIAHlkooFhagGuQSvnhnXTfCoiOK/t7XCtOZwy8mHpUhkwNzIX+FGrqh1cIIITFsSM16slqyxg6P+80XEE97ttcz/0jV9b6Cz9u8OlZz/nLE=";
        String privateKey = "32ty/V2moZ0VSTMldJU7nw==";
        String plainData = AlipayEncrypt.decryptContent(content, "AES", privateKey, "UTF-8");
        System.out.println(plainData);
        AliPayUserDecryptPhoneInfo phoneInfo = JSONObject.parseObject(plainData, AliPayUserDecryptPhoneInfo.class);
        if (StringUtil.isMobile(phoneInfo.getMobile())) {
            UserPhoneInfo userPhoneInfo = UserPhoneInfo.builder()
                    .mobile(phoneInfo.getMobile())
                    .build();
            System.out.println(userPhoneInfo);
        }
    }
}
